package com.jade.modules.workflow.impl;

import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.ejb.TransactionAttribute;
import javax.inject.Inject;
import javax.inject.Named;

import com.jade.framework.base.criterion.Criteria;
import com.jade.framework.base.criterion.Restrictions;
import com.jade.framework.base.util.PagingList;
import com.jade.journal.Journal;
import com.jade.journal.JournalEntityManager;
import com.jade.journal.JournalUtils;
import com.jade.modules.workflow.*;
import org.springframework.transaction.annotation.Transactional;

/**
 * 简单流程管理服务实现。
 */
//@Named ("module_workflowService")
public class FlowServiceImpl implements FlowService
{
    private FlowDao flowDao;
    private Map<String, FlowFunction> functionMap;
    private JournalEntityManager journalEntityManager;

    @Inject
    public void setFlowDao(@Named ("module_workflowDao") FlowDao flowDao)
    {
        this.flowDao = flowDao;
    }

    public void setFunctionMap(Map<String, FlowFunction> functionMap)
    {
        this.functionMap = functionMap;
    }

//    @Inject
    public void setJournalEntityManager(
            @Named ("system_journalEntityManager") JournalEntityManager journalEntityManager)
    {
        this.journalEntityManager = journalEntityManager;
    }

    public FlowContext buildContext(String type, String entity)
            throws FlowException
    {
        FlowContext context = new FlowContext();
        context.setType(type);
        context.setCurrentStep(1);
        context.setEntity(entity);
        context.setTotalStep(getTotalStep(type));
        if (context.getTotalStep() > 1) {
            flowDao.addContext(context);
        }
        return context;
    }

    protected int getTotalStep(String type)
    {
        List<FlowRole> roles = listRole(type);
        return roles.size();
    }

    protected FlowFunction getFunction(String type)
    {
        return functionMap.get(type);
    }

    protected Journal journal = JournalUtils.getJournal(FlowRecord.NAME);

    protected void journalRecord(String operatorType, String operator, String type, String entity,
                                 int result, String remark)
    {
        FlowRecord record = new FlowRecord();
        record.setOperatorType(operatorType);
        record.setOperator(operator);
        record.setType(type);
        record.setEntity(entity);
        record.setResult(result);
        record.setRemark(remark);
        record.setExecuteDate(new Date());
        journal.infoEntity(record);
    }


    @TransactionAttribute
    @Transactional (rollbackFor = Throwable.class)
    public void execute(String operatorType, String operator, String type, String entity, int step, int result,
                        String remark)
            throws FlowException
    {
        execute(operatorType, operator, type, entity, step, result, remark);

    }

    @TransactionAttribute
    @Transactional (rollbackFor = Throwable.class)
    public void execute(String operatorType, String operator, long applyId, int step, int result, String remark)
            throws FlowException
    {
        FlowApply apply = getApply(applyId);
        execute(operatorType, operator, apply.getType(), String.valueOf(applyId), apply, step, result, remark);
    }

    protected void execute(String operatorType, String operator, String type, String entity, FlowApply apply, int step,
                           int result, String remark)
            throws FlowException
    {
        //先获取上下文信息
        FlowContext context = getContext(type, entity);


        if (context == null) {
            if (step != 1) {
                throw new FlowException("err.module.workflow.wrong_step", "This flow has been not stated", null,
                        type, entity);
            }
            //第一个步骤，建立上下文信息
            context = buildContext(type, entity);

        } else {
            if (step != context.getCurrentStep() + 1 || step > context.getTotalStep()) {
                throw new FlowException("err.module.workflow.wrong_step", "This flow has been not stated", null,
                        type, entity);
            }

            context.setCurrentStep(step);
            if (context.getCurrentStep() == context.getTotalStep()) {
                flowDao.removeContext(context);
            } else {
                flowDao.updateContext(context);
            }
        }

        //调用业务代码
        if (apply != null) {
            apply.setStep(step);
            apply.setResult(result);
            flowDao.updateApply(apply);
        }
        context.setApply(apply);
        FlowFunction function = getFunction(type);
        function.execute(context, operatorType, operator, result, remark);
        //记录流程日志
        journalRecord(operatorType, operator, type, entity, result, remark);

    }

    public FlowContext getContext(String type, String entity)
    {
        return flowDao.getContext(type, entity);
    }

    public FlowContext getContext(String type, long applyId)
    {
        return getContext(type, String.valueOf(applyId));
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Throwable.class)
    public void addApply(FlowApply apply)
            throws FlowException
    {
        flowDao.addApply(apply);
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Throwable.class)
    public void updateApply(FlowApply apply)
            throws FlowException
    {
        flowDao.updateApply(apply);
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Throwable.class)
    public void removeApply(long... ids)
            throws FlowException
    {
        for (long id : ids) {
            FlowApply apply = getApply(id);
            if (apply != null) {
                flowDao.removeApply(apply);
            }
        }
    }

    public FlowApply getApply(long id)
    {
        return flowDao.getApply(id);
    }


    public PagingList<FlowApply> queryApply(String type, Criteria criteria)
    {
        return flowDao.queryApply(type, criteria);
    }

    public PagingList<FlowRecord> queryRecord(String type, String entity, Criteria criteria)
    {
        criteria.add(Restrictions.eq("type", type));
        criteria.add(Restrictions.eq("entity", entity));
        return journalEntityManager.queryEntity(FlowRecord.class, criteria);
    }

    public PagingList<FlowRecord> queryRecord(String type, long applyId, Criteria criteria)
    {
        return queryRecord(type, String.valueOf(applyId), criteria);
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Throwable.class)
    public void addRole(String type, int step, String roleIds)
            throws FlowException
    {
        //判断step是按照顺序的
        List<FlowRole> flowRoles = listRole(type);
        if ((flowRoles.isEmpty() && step > 1) || (!flowRoles.isEmpty() && step != (flowRoles.get(
                flowRoles.size() - 1).getStep()) + 1)) {
            throw new FlowException("err.module.workflow.wrong_step", "step must be ordered", null, type, step);
        }
        FlowRole role = new FlowRole();
        role.setType(type);
        role.setStep(step);
        role.setRoles(roleIds);
        flowDao.addRole(role);
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Throwable.class)
    public void setRole(String type, int step, String roles)
            throws FlowException
    {
        FlowRole role = flowDao.getRole(type, step);
        role.setRoles(roles);
        flowDao.updateRole(role);
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Throwable.class)
    public void clearRole(String type, int step)
            throws FlowException
    {
        flowDao.removeRole(type, step);
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Throwable.class)
    public void clearRole(String type)
            throws FlowException
    {
        flowDao.removeRole(type, -1);
    }

    public FlowRole getRole(String type, int step)
    {
        return flowDao.getRole(type, step);
    }

    public List<FlowRole> listRole(String type)
    {
        return flowDao.listRole(type);
    }
}
